#include "rtc.h"
#include "delay.h"
#include <stdio.h>

//整个工程全局变量
calendar cal;
u8 RTC_ALARM_STATUS = 0;
//单个文件内全局变量
u32 RTC_Alarm_Value=0;

//RTC初始化函数
u8 RTC_Init(void){
	u8 temp=0;
	//通过备份数据寄存器里的值来判断是否是第一次配置RTC时钟
	if(BKP->DR1!=0x1234){		//0x1234为任意值
		RCC->APB1ENR|=1<<28;	//使能电源接口时钟
		RCC->APB1ENR|=1<<27;	//使能备份接口时钟
		PWR->CR|=1<<8;			//取消备份区域的写保护
		RCC->BDCR|=1<<16;		//备份区域软件复位
		RCC->BDCR&=~(1<<16);	//备份区域软件复位结束
		RCC->BDCR|=1<<0;		//使能外部低速振荡器
		while((!(RCC->BDCR&(1<<1))) && temp<250){	//等待外部时钟就绪
			temp += 1;
			delay_ms(10);
		}
		if(temp>=250){	//外部时钟初始化失败，外部晶振有问题
			return 1;
		}

		RCC->BDCR|=1<<8;	//LSE作为RTC时钟
		RCC->BDCR|=1<<15;	//RTC时钟使能
		while(!(RTC->CRL&(1<<5)));	//等待RTC寄存器操作完成
		while(!(RTC->CRL&(1<<3)));	//等待RTC寄存器同步
		RTC->CRH|=0x01;		//允许秒中断
		// RTC->CRH|=1<<1;		//允许闹钟中断（使用RTC内置闹钟失败，存在死机问题 2016.06.23）
		while(!(RTC->CRL&(1<<5)));	//等待RTC寄存器操作完成

		RTC->CRL|=1<<4;		//允许配置
		RTC->PRLH=0x0000;
		RTC->PRLL=0x7fff;	//时钟周期设置,输入时钟频率为32.768kHz时为0x7fff。
		RTC->CRL&=~(1<<4);	//退出配置模式，开始更新RTC寄存器
		while(!(RTC->CRL&(1<<5)));	//等待RTC寄存器操作完成
		RTC_Set(2016, 6, 3, 0, 0, 0);	//设置时间
		BKP->DR1=0x1234;	//向备份区域写入标志
	}else{	//系统继续计时
		while(!(RTC->CRL&(1<<3)));	//等待RTC寄存器同步
		RTC->CRH|=0x01;				//允许秒中断
		// RTC->CRH|=1<<1;				//允许闹钟中断（使用RTC内置闹钟失败，存在死机问题 2016.06.23）
		while(!(RTC->CRL&(1<<5)));	//等待RTC寄存器操作完成
	}
	MY_NVIC_Init(0,0,RTC_IRQn,2);	//秒中断中断优先级设置
	RTC_Get();	//更新时间 
	return 0;
}

//RTC时钟中断函数
void RTC_IRQHandler(void){
	if(RTC->CRL&0x01){			//秒钟中断
		RTC_Get();				//更新时间
		if(RTC_Alarm_Value > 0){
			RTC_Alarm_Value -= 1;
			if(RTC_Alarm_Value==0){
				RTC_ALARM_STATUS = 1;
			}
		}
	}
	if(RTC->CRL&0x02){			//闹钟中断
		RTC->CRL&=~(0x02);		//清闹钟中断
	}
	RTC->CRL&=0x0ffa;			//清除溢出、秒钟中断标志
	while(!(RTC->CRL&(1<<5)));	//等待RTC寄存器操作完成
}
// void RTCAlarm_IRQHandler(void){
	// if(RTC->CRL&0x02){			//闹钟中断
		// RTC_Alarm_Status = 1;
		// RTC->CRL&=~(0x02);		//清闹钟中断
	// }
// }

//平年的月份日期表
const u8 mon_table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
//设置时钟函数
//以1970年1月1日为基准
//1970~2099年为合法年份
u8 RTC_Set(u16 year, u8 month, u8 day, u8 hour, u8 minute, u8 second){
	u16 temp;
	u32 secCount=0;
	if(year<1970 || year>2099){
		return 1;
	}
	for(temp=1970; temp<year; temp++){	//把所有年份的秒钟相加
		if(Is_Leap_Year(temp)){
			secCount+=31622400;	//闰年的秒钟数
		}else{
			secCount+=31536000;	//平年的秒钟数
		}
	}
	month -= 1;
	for(temp=0; temp<month; temp++){	//把前面月份的秒钟数相加
		secCount += (u32)mon_table[temp]*86400;	//月份秒钟数相加
		if(Is_Leap_Year(year)&&temp==1){
			secCount+=86400;	//闰年2月份增加一天的秒钟数
		}
	}
	secCount += (u32)(day-1)*86400;	//把前面日期的秒钟数相加 
	secCount += (u32)hour*3600;		//小时秒钟数
	secCount += (u32)minute*60;		//分钟秒钟数
	secCount += second;				//最后的秒钟加上去

	secCount += 2;	//配置时会有2秒左右的延时

	//设置时钟
	RCC->APB1ENR|=1<<28;	//使能电源接口时钟
	RCC->APB1ENR|=1<<27;	//使能备份接口时钟
	PWR->CR|=1<<8;			//取消备份区域的写保护
	RTC->CRL|=1<<4;			//允许配置 
	RTC->CNTL=secCount&0xffff;
	RTC->CNTH=secCount>>16;
	RTC->CRL&=~(1<<4);	//退出配置模式，开始更新RTC寄存器
	while(!(RTC->CRL&(1<<5)));	//等待RTC寄存器操作完成 
	RTC_Get();	//设置完之后更新数据
	return 0; 
}

//判断是否是闰年函数
u8 Is_Leap_Year(u16 year){
	if(year%4==0){	//必须能被4整除
		if(year%100==0){ 
			if(year%400==0){
				return 1;	//如果以00结尾,还要能被400整除
			}else{
				return 0;
			}
		}else{
			return 1;
		}
	}else{
		return 0;
	}
}

//得到当前的时间函数
//结果保存在calendar结构体里
u8 RTC_Get(void){
	static u16 dayCount=0;
	u32 timeCount=0; 
	u32 t1=0;
	u16 t2=0;

	timeCount=RTC->CNTH;	//得到计数器中的值(秒钟数)
	timeCount<<=16;
	timeCount += RTC->CNTL;

	t1=timeCount/86400;	//得到天数(秒钟数对应的)
	if(dayCount!=t1){	//超过一天了  
		dayCount=t1;
		t2=1970;	//从1970年开始
		while(t1>=365){
			if(Is_Leap_Year(t2)){	//是闰年
				if(t1>=366){
					t1 -= 366;	//闰年的秒钟数
				}else{
					break;
				}
			}else{
				t1 -= 365;	//平年 
			}
			t2 += 1;  
		}
		cal.year=t2;//得到年份
		t2=0;
		while(t1>=28){	//超过了一个月
			if(Is_Leap_Year(cal.year)&&t2==1){	//是不是闰年的2月份
				if(t1>=29){
					t1 -= 29;	//闰年的天数
				}else{
					break; 
				}
			}else{	//平年的2月份和其他月份
				if(t1>=mon_table[t2]){
					t1 -= mon_table[t2];
				}else{
					break;
				}
			}
			t2 += 1;
		}
		cal.month=t2+1;	//得到月份
		cal.day=t1+1;	//得到日期 
	}
	t1=timeCount%86400;	//得到剩余一天里的秒钟数
	cal.hour=t1/3600;		//得到小时
	cal.minute=(t1%3600)/60;	//得到分钟
	cal.second=(t1%3600)%60;	//得到秒钟
	cal.week=RTC_Get_Week(cal.year, cal.month, cal.day);	//获取星期
	return 0;
}

//月修正数据表
u8 const table_week[12]={0,3,3,6,1,4,6,2,5,0,3,5};
//获取星期函数(只允许1901-2099年)
u8 RTC_Get_Week(u16 year, u8 month, u8 day){
	u16 temp;
	u8 yearH,yearL;
	yearH=year/100;
	yearL=year%100; 
	//如果为21世纪,年份数加100
	if(yearH>19){
		yearL+=100;
	}
	//所过闰年数只算1900年之后的  
	temp=yearL+yearL/4;
	temp=temp%7; 
	temp=temp+day+table_week[month-1];
	if(yearL%4==0&&month<3){
		temp--;
	}
	return (temp%7);
}

u8 RTC_Set_Calendar(calendar c){
	return RTC_Set(c.year, c.month, c.day, c.hour, c.minute, c.second);
}

//RTC重置
void RTC_Reset(void){
	RCC->APB1ENR|=1<<28;	//使能电源接口时钟
	RCC->APB1ENR|=1<<27;	//使能备份接口时钟
	PWR->CR|=1<<8;			//取消备份区域的写保护
	RCC->BDCR|=1<<16;		//备份区域软件复位
	RCC->BDCR&=~(1<<16);	//备份区域软件复位结束
	BKP->DR1=0x8888;		//向备份区域写入其他值

	//STM32软件重启
	__disable_fault_irq();
	NVIC_SystemReset();
}

//从格式化字符串中取出时间
u8 String_To_Calendar(u8 *s, u16 l, calendar *c){
	u16 temp;
	if(l < 20){
		return 1;
	}
	for(temp=0; temp < l; temp++){	//将ASCII码转为数字
		s[temp] -= '0';
	}
	c->year = s[0]*1000+s[1]*100+s[2]*10+s[3];
	c->month = s[5]*10+s[6];
	c->day = s[8]*10+s[9];
	c->hour = s[11]*10+s[12];
	c->minute = s[14]*10+s[15];
	c->second = s[17]*10+s[18];
	// printf("%d %d %d %d %d %d\r\n", c->year,c->month,c->day,c->hour,c->minute,c->second);
	return 0;
}

//星期的英文缩写(6个字符，不够加空格补齐)
const unsigned char *weekAb[7] = {"Sun. ", "Mon. ", "Tues.", "Wed. ", "Thur.", "Fri. ", "Sat. "};
//把时间转换为格式化字符串
u8 Calendar_To_String(calendar *c, u8 *s, u16 l){
	u16 temp;
	if(l < 26){
		return 1;
	}
	s[0] = c->year / 1000;
	s[1] = c->year % 1000 / 100;
	s[2] = c->year % 100 / 10;
	s[3] = c->year % 10;
	s[5] = c->month / 10;
	s[6] = c->month % 10;
	s[8] = c->day / 10;
	s[9] = c->day % 10;

	s[17] = c->hour / 10;
	s[18] = c->hour % 10;
	s[20] = c->minute / 10;
	s[21] = c->minute % 10;
	s[23] = c->second / 10;
	s[24] = c->second % 10;

	for(temp=0; temp < l; temp++){	//将数字转为ASCII码
		s[temp] += '0';
	}

	for(temp=0; temp < 5; temp++){	//加入星期的ASCII码，舍去'\0'
		s[temp+11] = weekAb[cal.week][temp];
	}

	//加入其它字符
	s[4] = s[7] = '/';
	s[10] = s[16] = ' ';
	s[19] = s[22] = ':';
	s[25] = '\0';

	return 0;
}

//把时间转换为日期字符串和时间字符串
u8 Calendar_To_Date_Time(calendar *c, u8 *d, u16 ld, u8 *t, u16 lt){
	u16 temp;
	u8 str[30];
	u16 len = 30;
	if(ld < 16 || lt < 9){
		return 1;
	}
	Calendar_To_String(c, str, len);
	for(temp=0; temp<16; temp++){
		d[temp] = str[temp];
	}
	d[temp] = '\0';
	for(temp=0; temp<8; temp++){
		t[temp] = str[temp+17];
	}
	t[temp] = '\0';

	return 0;
}

//打印时间
void RTC_Print_Calendar(calendar *c){
	u8 str[30];
	u16 len = 30;
	Calendar_To_String(c, str, len);
	printf("%s\r\n", str);
}

//设置闹钟值（使用RTC内置闹钟失败，存在死机问题 2016.06.23）
/* void RTC_Set_Alarm(u32 alarmValue){
	RTC->CRL|=1<<4;		//允许配置
	RTC->ALRH=RTC->CNTH+(alarmValue>>16);
	RTC->ALRL=RTC->CNTL+(alarmValue&0x0000ffff);
	RTC->CRL&=~(1<<4);	//退出配置模式，开始更新RTC寄存器
	while(!(RTC->CRL&(1<<5)));	//等待RTC寄存器操作完成
} */

//设置闹钟值
void RTC_Set_Alarm(u32 hours){
	RTC_Alarm_Value = hours * 3600;
	// RTC_Alarm_Value = hours;
}
